package com.seeqool.v1.datamodel;

import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Vector;

import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;

enum ColType {TEXT, NUMBER, DATE, UNKNOWN};  

public class SQLTable implements TableModel {
	
	private String[] colNames;
	private ColType[] colTypes; 
	private Vector<SQLValues> data;
	private int colCount;
	private int rowCount;
	private char[] colIDs;
	
	public SQLTable(ResultSet dataSet) throws SQLException {
		// determine Table Structure
		ResultSetMetaData metaData = dataSet.getMetaData();
		colCount=metaData.getColumnCount();
		
		int curSize = 100;
		colNames = new String[colCount];
		colIDs   = new char[colCount]; 
		colTypes = new ColType[colCount];
		data = new Vector<SQLValues>();
		for (int col=1; col<=colCount; col++) {
			String colName = metaData.getColumnLabel(col);
			if ((colName.length()==1) || ((colName.length()>1) && (colName.charAt(1)=='_'))) {
				colIDs[col-1] = colName.charAt(0);
				if (colName.length()>2) colName = colName.substring(2); else colName = "";
			} else {
				colIDs[col-1] = ' ';
			}
			String cn = colName;
			int id = 1;
			while (id>0) {
				if (id==1) cn=colName; else cn=colName+"["+id+"]";
				boolean bad = false;
				for (int j=0;j<col-1;j++)
					bad |= colNames[j].toUpperCase().equals(cn);
				if (bad) {
					id++;
				} else {
					id=-1;
				}
			}
			colNames[col-1] = cn;
			switch (metaData.getColumnType(col)) {
				case Types.TINYINT:
				case Types.SMALLINT:
				case Types.INTEGER:
				case Types.BIGINT:
				case Types.REAL:
				case Types.FLOAT:
				case Types.DOUBLE:
				case Types.DECIMAL:
				case Types.NUMERIC:
				case Types.BIT:
					colTypes[col-1] = ColType.NUMBER;
					data.add(new SQLFloats(curSize));
					break;
				case Types.DATE:
				case Types.TIME:
				case Types.TIMESTAMP:
					colTypes[col-1] = ColType.DATE;
					data.add(new SQLDates(curSize));
					break;
				case Types.CHAR:
				case Types.VARCHAR:
				case Types.LONGVARCHAR:
				case Types.BINARY:
				case Types.VARBINARY:
				case Types.LONGVARBINARY:
					colTypes[col-1] = ColType.TEXT;
					data.add(new SQLStrings(curSize));
					break;
				default:
					colTypes[col-1] = ColType.UNKNOWN;
					data.add(null);
			}
		}
		
		// copy data
		rowCount=0;
		while (dataSet.next()) {
			if (rowCount==curSize) {
				curSize*=2;
				for (int col=0; col<colCount; col++)
					data.get(col).doubleSize();
			}
			for (int col=0; col<colCount; col++) {
				switch (colTypes[col]) {
					case TEXT:
						((SQLStrings) data.get(col)).set(rowCount, dataSet.getString(col+1));
						break;
					case NUMBER:
						((SQLFloats) data.get(col)).values[rowCount] = dataSet.getFloat(col+1);
						break;
					case DATE:
						((SQLDates) data.get(col)).values[rowCount] = dataSet.getTimestamp(col+1);
						break;
				}
			}
			rowCount++;
		}
		if (rowCount!=curSize) {
			for (int col=0; col<colCount; col++)
				data.get(col).setSize(rowCount);
		}
	}
	
	public char getColID(int columnIndex) {
		if ((columnIndex<0) || (columnIndex>=colNames.length)) return ' ';
		return colIDs[columnIndex];
	}

	public Class<?> getColumnClass(int columnIndex) {
		if ((columnIndex<0) || (columnIndex>=colNames.length)) return null;
		switch (colTypes[columnIndex]) {
			case TEXT:	  return String.class; 
			case NUMBER:  return Float.class;
			case DATE:    return Date.class;
		}
		return null;
	}

	public int getColumnCount() {
		return colCount;
	}

	public String getColumnName(int columnIndex) {
		if ((columnIndex<0) || (columnIndex>=colNames.length)) return null;
		return colNames[columnIndex];
	}

	public int getRowCount() {
		return rowCount;
	}

	public Object getValueAt(int rowIndex, int columnIndex) {
		if ((columnIndex<0) || (columnIndex>=colTypes.length)) return null;
		if ((   rowIndex<0) || (   rowIndex>=rowCount       )) return null;
		switch (colTypes[columnIndex]) {
			case TEXT:	  return ((SQLStrings) data.get(columnIndex)).get   (rowIndex);
			case NUMBER:  return new Float(((SQLFloats)  data.get(columnIndex)).values[rowIndex]);
			case DATE:    return ((SQLDates)   data.get(columnIndex)).values[rowIndex];
		}
		return null;
	}

	public boolean isCellEditable(int rowIndex, int columnIndex) {
		return false;
	}

	public void addTableModelListener(TableModelListener l) {}
	public void removeTableModelListener(TableModelListener l) {}
	public void setValueAt(Object aValue, int rowIndex, int columnIndex) {}
	
	public float[] getFloats(int columnIndex) {
		return ((SQLFloats) data.get(columnIndex)).values;
	}
	
	public float[][] getFloats2D(int columnIndexX, int columnIndexY) {
		float[][] dat = {((SQLFloats) data.get(columnIndexX)).values, 
				         ((SQLFloats) data.get(columnIndexY)).values};
		return dat;
	}
}
